<!DOCTYPE html>
<html>

<head>
    <title>LUT</title>
    <script type="text/javascript" src="https://cdn.bootcss.com/axios/0.18.0/axios.min.js"></script>
    <style type="text/css">
        canvas {
            width: 600px;
        }
    </style>
</head>

<body>
    <video src="" style="width:200px;height:200px"></video>
    <script>

// function cameraStart() {
//             let isFront = false;

//             let config = {
//                 audio: false, video: true, video: {
//                     width: 700,
//                     height: 500,
//                     // 前后置摄像头
//                     facingMode: isFront ? "user" : "environment",
//                     // 帧率设置. 字面意思，一个是理想的状态下面，一个是最大的帧率
//                     frameRate: { ideal: 30, max: 30 }
//                 },
//             };
//             let video = document.querySelector("video");
//             function successCallback(stream) {
//                 // 将返回的流提供给控制台进行检查
//                 window.stream = stream;
//                 console.log(stream)
//                 video.srcObject = stream;
//                 // 播放
//                 video.play();
//             }
//             function errorCallback(error) {
//                 console.log("navigator.getUserMedia error: ", error);
//             }
//             // 传入3个参数，第一个是配置，第二个是成功的回调
//             // 这个更加规范了，多加了一个mediaDevices。window.navigator.getUserMedia(config, successCallback, errorCallback);
//             navigator.mediaDevices.getUserMedia(config)
//                 .then(function (stream) {
//                     successCallback(stream)
//                 })
//                 .catch(function (err) {
//                     errorCallback(err)
//                 });

//         }
//         // 调用
//         cameraStart()

        function getTable(url) {
            return axios(url, {
                method: 'GET',
            })
                .then(res => {
                    const tableString = res.data;
                    // 按行分割字符串
                    const tempArr = tableString.split('\n');
                    let lut_3d_size = 0;
                    let start = -1;

                    const table = [], resTable = []

                    for (let i = 0; i < tempArr.length; i++) {
                        const str = tempArr[i];
                        // 获取采样数量
                        if (str.includes('LUT_3D_SIZE')) {
                            lut_3d_size = +str.replace('LUT_3D_SIZE', '');
                            continue;
                        }

                        // 将空节点与文件头过滤掉
                        if (!str || /[a-z]/i.test(str)) continue;

                        // 得到色彩数据开始的索引
                        if (start === -1) {
                            start = i;
                        }

                        // 计算色彩数据真实的索引  重要：这里难理解但是还好，就是说没有索引是注释的序号的
                        const idx = i - start;

                        // 分割rgb的值
                        const pixel = str.split(' ').map(s => Number(s)); //[ 3个值 ]
                        console.log("idx / lut_3d_size",idx , lut_3d_size) 
                        // 根据table的排列规律，创建二维数组(65 * 65 * 65),这里我们根据从文件中实际获取到的采样数来计算
                        if (!table[Math.floor(idx / lut_3d_size)]) table[Math.floor(idx / lut_3d_size)] = [];
                        // 重要：第一次拿到值
                        table[Math.floor(idx / lut_3d_size)].push(pixel);
                    }

                    for (let idx = 0; idx < table.length; idx++) {
                        const piece = table[idx]; // [ 65个值 ]
                        // console.log("piece",piece)
                        if (!resTable[Math.floor(idx / lut_3d_size)]) resTable[Math.floor(idx / lut_3d_size)] = [];
                        resTable[Math.floor(idx / lut_3d_size)].push(piece);
                    }
                    console.log("result:", {
                        table: resTable, //64 * 63 *3
                        size: lut_3d_size
                    })
                    return {
                        table: resTable,
                        size: lut_3d_size
                    }

                })
                .catch(err => {
                    console.error(err)
                })
        }



        function mix(x, y, b) {
  const a = b - Math.floor(b);
  return Math.floor((x * (1 - a) + y * a) * 255);
}
        



function lut3d(targetColor, table, lut3dSize) {
  const [r, g, b] = targetColor || [];

  const tr = r / 255;
  const tg = g / 255;
  const tb = b / 255;

  // 计算最大索引值
  const n = lut3dSize - 1;
  // 计算blue索引
  const b_index = tb * n;
  // 计算red索引
  const r_index = Math.floor(tr * n);
  // 计算green索引
  const g_index = Math.floor(tg * n);

  // 计算blue的离散索引
  const b_floor_idx = Math.floor(b_index);
  const b_ceil_idx = Math.ceil(b_index);

  // 找到blue所在的数据
  const b_ceil = table[b_ceil_idx];
  const b_floor = table[b_floor_idx];

  // 找到green所在的数据
  const g_ceil = b_ceil[g_index];
  const g_floor = b_floor[g_index];

  // 找到red所在的数据， red对应的点，为将要替换的rgb目标数据
  const r_ceil = g_ceil[r_index];
  const r_floor = g_floor[r_index];

  return [
    mix(r_ceil[0], r_floor[0], tb),
    mix(r_ceil[1], r_floor[1], tb),
    mix(r_ceil[2], r_floor[2], tb),
  ]
}




// 重要：上面的是工具方法，下面的才是主要逻辑

        const video_url = 'https://cdn.jsdelivr.net/npm/electroluxasset/video/origin.mp4';

        const test_cube_file = 'Cinematic 04__OXYGENART.cube';


    getTable(test_cube_file).then((res) => {
      const { table, size } = res;
      console.log(res)
      const canvas = document.createElement("canvas");
      const video = document.createElement("video");
      const play_button = document.createElement("button");

      play_button.innerHTML = '播放';

      canvas.style.cssText = `
      position:absolute;
      top:50%;
      left:50%;
      transform:translate(-110%,-50%);
      border:1px solid #333;
      z-index:9999999;
    `;

      video.style.cssText = `
      position:absolute;
      top:50%;
      left:50%;
      transform:translate(10%,-50%);
      border:1px solid #333;
      z-index:9999999;
    `
      play_button.style.cssText = `
      position:absolute;
      top:50%;
      left:50%;
      transform:translate(-50%,-50%);
      border:1px solid #333;
      z-index:9999999;
    `
      const ctx = canvas.getContext("2d");

      video.crossOrigin = 'anonymous';
      video.src = video_url;
        // video.autoplay = true
      video.oncanplaythrough = () => {
        canvas.width = video.videoWidth;
        canvas.height = video.videoHeight;
        video.loop = true;

        checkVideo();
      }
      
      function checkVideo() {
        
        ctx?.drawImage(video, 0, 0, video.videoWidth, video.videoHeight);
        const video_image_data = ctx?.getImageData(0, 0, canvas.width, canvas.height);
        const imageData = new ImageData(video_image_data.width, video_image_data.height)
        const video_pixel_data = video_image_data.data;

        for (let i = 0; i < imageData.data.length; i += 4) {
          // 基底素材的pixel
          const vr = video_pixel_data[i];
          const vg = video_pixel_data[i + 1];
          const vb = video_pixel_data[i + 2];
          const va = video_pixel_data[i + 3];

        //   当前
          const [r, g, b] = lut3d([vr, vg, vb], table, size);

          imageData.data[i] = r
          imageData.data[i + 1] = g
          imageData.data[i + 2] = b
          imageData.data[i + 3] = va;
        }

        ctx?.putImageData(imageData, 0, 0);
        window.requestAnimationFrame(checkVideo)
      }



      play_button.onclick = () => {
        video.play();
      }


      document.body.appendChild(canvas);
      document.body.appendChild(video);
      document.body.appendChild(play_button);

    });






    </script>
</body>

</html>